﻿/*
 * This file is part of CommunitySettlers.
 *
 * CommunitySettlers is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.

 * CommunitySettlers is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.

 * You should have received a copy of the GNU General Public License
 * along with CommunitySettlers.  If not, see <http://www.gnu.org/licenses/>.
 * 
 * Author: Christoph Husse
 * 
 * Also checkout our homepage: http://communitysettlers.codeplex.com/
 */
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using System.Collections.ObjectModel;
using Microsoft.Xna.Framework.Input;

namespace SettlersEngine
{
    public delegate void DKeyboardHandler(Keys inSender);

    public static class Keyboard
    {
        private static System.Windows.Threading.DispatcherTimer m_KeyboardTimer = new System.Windows.Threading.DispatcherTimer();
        private readonly static List<Keys> m_LastState = new List<Keys>();
        private readonly static KeyInfo[] m_KeyInfo = new KeyInfo[256];
        private readonly static System.Diagnostics.Stopwatch m_Watch = new System.Diagnostics.Stopwatch();

        public static event DKeyboardHandler OnKeyRepeat;
        public static event DKeyboardHandler OnKeyDown;
        public static event DKeyboardHandler OnKeyUp;
        public static ReadOnlyCollection<Keys> PressedKeys { get; private set; }
        public static TimeSpan RepeatDelay { get; set; }
        public static TimeSpan RepeatInterval { get; set; }

        public class KeyInfo
        {
            public Keys Key { get; private set; }
            public TimeSpan LastDownTime { get; set; }
            public TimeSpan LastRepeatTime { get; set; }
            public TimeSpan RepeatDelay { get; set; }
            public TimeSpan RepeatInterval { get; set; }

            public KeyInfo(Keys inKey)
            {
                Key = inKey;
                RepeatDelay = TimeSpan.FromMilliseconds(30);
                RepeatInterval = TimeSpan.FromMilliseconds(30);
            }
        }

        static Keyboard()
        {
            PressedKeys = new ReadOnlyCollection<Keys>(m_LastState);
            m_Watch.Start();

            for (int i = 0; i < m_KeyInfo.Length; i++)
            {
                m_KeyInfo[i] = new KeyInfo((Keys)i);
            }
        }

        public static void Initialize()
        {
            m_KeyboardTimer.Interval = TimeSpan.FromMilliseconds(30);
            m_KeyboardTimer.Tick += OnKeyboardTimer_Tick;
            m_KeyboardTimer.Start();
        }

        static void OnKeyboardTimer_Tick(object sender, EventArgs e)
        {
            List<Keys> downEvents = new List<Keys>();
            List<Keys> upEvents = new List<Keys>();
            Keys[] xnaKeys = Microsoft.Xna.Framework.Input.Keyboard.GetState().GetPressedKeys();
            TimeSpan downTime = m_Watch.Elapsed;

            foreach (var key in xnaKeys)
            {
                KeyInfo info = m_KeyInfo[(int)key];

                if (!m_LastState.Contains(key))
                {
                    downEvents.Add(key);
                    info.LastDownTime = downTime;
                }
                else
                {
                    // check for repeat
                    if (downTime - info.LastDownTime < info.RepeatDelay)
                        continue;

                    if (downTime - info.LastRepeatTime < info.RepeatInterval)
                        continue;

                    info.LastRepeatTime = downTime;

                    if (OnKeyRepeat != null)
                        OnKeyRepeat(key);
                }
            }

            foreach (var key in m_LastState)
            {
                if (!xnaKeys.Contains(key))
                    upEvents.Add(key);
            }

            m_LastState.Clear();
            m_LastState.AddRange(xnaKeys);

            foreach (var key in downEvents)
            {
                if (OnKeyDown != null)
                    OnKeyDown(key);
            }

            foreach (var key in upEvents)
            {
                if (OnKeyUp != null)
                    OnKeyUp(key);
            }
        }
    }
}
